import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from '../types'
import { createError } from '../helper/error'
import { isURLSameOrigin } from '../helper/url'
import cookie from '../helper/cookie'
import { isFormData } from '../helper/util'

export default function xhr(config: AxiosRequestConfig): AxiosPromise {
  return new Promise((resolve, reject) => {
    const {
      url,
      data = null,
      method = 'GET',
      headers,
      responseType,
      timeout,
      withCredentials,
      xsrfCookieName,
      xsrfHeaderName,
      auth,
      cancelToken,
      onDownloadProgress,
      onUploadProgress,
      validateStatus
    } = config

    // 1.创建 xhr 实例
    const xhr = new XMLHttpRequest()

    // 2.初始化 xhr.open 方法
    xhr.open(method.toUpperCase(), url!, true)

    // 3.处理 config 配置
    configureRequest()

    // 4.添加事件处理函数
    addEvents()

    // 5.处理请求 headers
    processHeaders()

    // TODO 6.处理取消请求逻辑
    processCancel()

    // 7.发送请求
    xhr.send(data)

    function handleResponse(response: AxiosResponse): void {
      if (!validateStatus || validateStatus(response.status)) {
        resolve(response)
      } else {
        reject(
          createError(
            `Request failed with status code ${response.status}`,
            config,
            null,
            xhr,
            response
          )
        )
      }
    }
    function configureRequest(): void {
      if (responseType) {
        xhr.responseType = responseType
      }

      if (timeout) {
        xhr.timeout = timeout
      }

      if (withCredentials) {
        xhr.withCredentials = withCredentials
      }

      if (auth) {
        headers['Authorization'] = `Basic ${btoa(auth.username + ':' + auth.password)}`
      }
    }

    function addEvents(): void {
      xhr.onreadystatechange = function() {
        if (xhr.readyState !== 4) {
          return
        }

        if (xhr.status === 0) {
          return
        }
        const responseHeaders = xhr.getAllResponseHeaders()

        const responseDate =
          responseType && responseType !== 'text' ? xhr.response : xhr.responseText

        const response: AxiosResponse = {
          data: responseDate,
          status: xhr.status,
          statusText: xhr.statusText,
          headers: responseHeaders,
          config: config,
          request: xhr
        }
        handleResponse(response)
      }

      // 处理 ajax ❌网络错误
      xhr.onerror = function() {
        reject(createError('Network Error', config, null, xhr))
      }
      //
      xhr.ontimeout = function() {
        reject(createError(`Request Timeout with ${timeout} ms`, config, 'ECONNABORTED', xhr))
      }
      if (onDownloadProgress) {
        xhr.onprogress = onDownloadProgress
      }
      if (onUploadProgress) {
        xhr.upload.onprogress = onUploadProgress
      }
    }

    function processHeaders(): void {
      if ((withCredentials || isURLSameOrigin(url!)) && xsrfCookieName) {
        const xsrfValue = cookie.read(xsrfCookieName)
        if (xsrfValue) {
          headers[xsrfHeaderName!] = xsrfValue
        }
      }

      if (isFormData(data)) {
        delete headers['Content-Type']
      }

      Object.keys(headers).forEach(name => {
        if (data === null && name.toLocaleLowerCase() === 'content-type') {
          delete headers[name]
        } else {
          xhr.setRequestHeader(name, headers[name])
        }
      })
    }
    // TODO
    function processCancel(): void {
      if (cancelToken) {
        cancelToken.promise
          .then(reason => {
            xhr.abort()
            reject(reason)
          })
          .catch(
            /** istanbul ignore next */
            () => {
              // do nothing
            }
          )
      }
    }
  })
}
